/*
 * sbi.h
 *
 *  Created on: 13.01.2020
 *      Author: microengineer18
 */

#ifndef SBI_H_
#define SBI_H_

#include <stdint.h>

#include "main.h"


enum SBIError {
	ERROR =0,
	SE_NOT_INITIALIZED = -1,
	INVALID_SLOT = -2,
	INVALID_KEY_INDEX = -3,
	INVALID_SECLEVEL = -4,
	ERROR_LOADING_KEY = -5,
	INVALID_KEY = -6,
	ERROR_START_SE = -7,
	ERROR_SET_RANDOM_KEY = -8,
	INVALID_BUNDLE_HASH = -9,
	INVALID_AUTH = -10,
	INSECURE_BUNDLE_HASH = -11,
	INVALID_TRYTES = -12,
	INVALID_BRANCH = -13,
	INVALID_TRUNK = -14,
	INVALID_MWM = -15,
	TOO_MANY_TX = -16,
	INVALID_ADDRESS = -17,
	INVALID_TAG = -18,
	DATA_TOO_LONG = -19,
	ERROR_READING_FLASH = -20,
	INVALID_FLASH_PAGE = -21,
	INVALID_BASE64_DATA = -22,
	ERROR_WRITING_FLASH = -23,
	INVALID_JSON = -24,
	TOO_FEW_TX = -25,
	TOO_MANY_ADDRESSES = -26,
	INVALID_COUNT = -27,
	INVALID_BIP_PATH = -28,
	NO_ACTIVE_SEED = -29,
	INVALID_TRANSFERS = -30,
	INVALID_INPUTS = -31,
	INVALID_REMAINDER = -32,
	INVALID_VALUE = -33,
	INSUFFICIENT_TOKENS = -34,
	UNSPENT_TOKENS = -35,
//	MISSING_PARAMETER = -36,
//	INVALID_PARAMETER = -37,
	MISSING_OR_INVALID = -38,
	SEED_NOT_ACTIVATED = -39,
	INVALID_MESSAGE = -40,
	UNKNOWN_CALL = -127,
	NONE = 1,
	SE_ALREADY_INITIALIZED = 2
};

class SBIRequest {
public:
};

class SBIResponse {
//protected:
public:
	SBIError code;
public:
	void error(SBIError code) {
		this->code = code;
	}
	bool isError() { return int(code) < 1;  }
	const char* errorMsg();
	static const char* errorMsg(SBIError code);

};

// uses transaction-memory to not waste additional space
// for this reason, it gives back an array of pointers
// to the signature fragments
class Transaction;


//--------------------------------------------------------------------
class DoPoWRequest : public SBIRequest  {
public:
	const char* 	trytes;
	uint32_t 		mwm;
};

class DoPoWResponse : public SBIResponse {
public:
	char			nonce[28];		// with zero terminator
};
//--------------------------------------------------------------------

class CurlHashRequest : public SBIRequest  {
public:
	const char* 	trytes;
};

class CurlHashResponse : public SBIResponse {
public:
	char			hash[82];		// with zero terminator
	uint32_t		mwm;
};

//--------------------------------------------------------------------

class SignBundleHashRequest : public SBIRequest  {
public:
	uint32_t 		slot;
	uint32_t 		security;
	uint32_t 		keyIndex;
	const char* 	bundleHash;
	const uint8_t* 	auth;
};

class SignBundleHashResponse : public SBIResponse {
public:
	Transaction*	signaturFragments[3];
};

//--------------------------------------------------------------------

class TestAccelerationResponse : public SBIResponse {
public:
	bool 			pow;
	bool 			curl;
	bool 			keccak384;
	bool 			troika;
	bool			sha512;

	bool 			bigintToTritsRandomRepeated;
	bool 			tritsToBigintRandomRepeated;

	bool 			trytesToBigintRandomRepeated;
	bool 			bigintToTrytesRandomRepeated;
};

//--------------------------------------------------------------------

class ReadFlashRequest : public SBIRequest  {
public:
	uint32_t 		page;
};

// memory space of transaction array will be used
class ReadFlashResponse : public SBIResponse {
public:
	uint8_t*		data;
};

//--------------------------------------------------------------------

class SetRandomSeedRequest : public SBIRequest {
public:
	uint32_t 		slot;
};

//--------------------------------------------------------------------

class GetRandomBytesResponse : public SBIResponse {
public:
	uint8_t			bytes[48];
};

//--------------------------------------------------------------------

class GetAddressRequest : public SBIRequest  {
public:
	uint32_t 		slot;
	uint32_t 		security;
	uint32_t 		keyIndex;
	bool 			checksum;
	bool 			display;
};

class GetAddressResponse : public SBIResponse {
public:
	char 			address[91];	// incl zero terminator
};

//--------------------------------------------------------------------

class PrepareTransfersRequest : public SBIRequest  {
public:
	uint32_t 		timestamp;
	uint32_t 		slot;
	uint32_t 		security;
	uint32_t 		numTX;
	const uint8_t* 	auth;
};

class PrepareTransfersResponse : public SBIResponse {
public:
	Transaction*	first;
	char			bundleHash[82];	// incl zero terminator
};

//--------------------------------------------------------------------

class InitSecureElementRequest : public SBIRequest  {
public:
	const char* 	key;
};

//--------------------------------------------------------------------

class WriteFlashRequest : public SBIRequest  {
public:
	uint32_t 		page;
	const uint8_t* 	auth;
};

//--------------------------------------------------------------------

class RebootRequest : public SBIRequest  {
public:
	uint32_t 		address;
};

//--------------------------------------------------------------------

class SetActiveSeedRequest : public SBIRequest  {
public:
	uint32_t 		path[4];
	uint32_t 		security;
};

//--------------------------------------------------------------------

class MCycleResponse : public SBIResponse {
public:
	uint64_t	ticks;
};


#define SBI_CALL(func, arg0, arg1, arg2, arg3, arg4, arg5) ({				\
	register uintptr_t a0 asm ("a0") = (uintptr_t)(func);					\
	register uintptr_t a1 asm ("a1") = (uintptr_t)(arg0);					\
	register uintptr_t a2 asm ("a2") = (uintptr_t)(arg1);					\
	register uintptr_t a3 asm ("a3") = (uintptr_t)(arg2);					\
	register uintptr_t a4 asm ("a4") = (uintptr_t)(arg3);					\
	register uintptr_t a5 asm ("a5") = (uintptr_t)(arg4);					\
	register uintptr_t a6 asm ("a6") = (uintptr_t)(arg5);					\
	asm volatile ("ecall"													\
		      : "+r" (a0)													\
		      : "r" (a1), "r" (a2), "r" (a3), "r" (a4), "r" (a5), "r" (a6)	\
		      : "memory");													\
	a0;																		\
})


/* Lazy implementations until SBI is finalized */
#define SBI_CALL_0(which) \
	      SBIError(SBI_CALL(which, 0, 0, 0, 0, 0, 0))
#if 0
#define SBI_CALL_1(which, arg0) \
	      SBI_CALL(which, arg0, 0, 0, 0, 0, 0)

#define SBI_CALL_2(which, arg0, arg1) \
	      SBI_CALL(which, arg0, arg1, 0, 0, 0, 0)

#define SBI_CALL_3(which, arg0, arg1, arg2) \
	      SBI_CALL(which, arg0, arg1, arg2, 0, 0, 0)

#define SBI_CALL_4(which, arg0, arg1, arg2, arg3) \
	      SBI_CALL(which, arg0, arg1, arg2, arg3, 0, 0)

#define SBI_CALL_5(which, arg0, arg1, arg2, arg3, arg4) \
	      SBI_CALL(which, arg0, arg1, arg2, arg3, arg4, 0)

#define SBI_CALL_6(which, arg0, arg1, arg2, arg3, arg4, arg5) \
	      SBI_CALL(which, arg0, arg1, arg2, arg3, arg4, arg5)
#endif

namespace SBI {
	enum SBI_CALL {
		NOP = 0,
		INIT_SECURE_ELEMENT = 1,
		SET_RANDOM_SEED = 2,
		SIGN_BUNDLEHASH	= 4,
		START_SECURE_ELEMENT = 5,
		POW = 6,
		CURL = 7,
		TEST_ACCELERATION = 8,
		READ_FLASH = 9,
		WRITE_FLASH = 10,
		REBOOT = 11,
		RANDOM_BYTES = 13,
		SET_ACTIVE_SEED_LC = 14,
		GET_ADDRESS_LC = 15,
		PREPARE_TRANSFERS_LC = 16,
		MCYCLE = 17,
		SWITCH_USERMODE = 0xff
	};


	// enters sbiCallHandler (is MMTEXT protected)
	SBIError sbiCall(uint32_t func);

	bool init();
	void switchUserMode();

	// methods for invoking sbi calls
	SBIResponse* initSecureElement(const char* key);
	SBIResponse* startSecureElement();
	SBIResponse* setRandomSeed(int slot);
	DoPoWResponse* doPoW(const char* trytes, uint32_t mwm);
	CurlHashResponse* curlHash(const char* trytes);
	SignBundleHashResponse* signBundleHash(uint32_t slot, uint32_t security, uint32_t keyIndex, const char* bundleHash, const uint8_t* auth);
	TestAccelerationResponse* testHardwareAcceleration();
	ReadFlashResponse* readFlash(uint32_t page);
	SBIResponse* writeFlash(uint32_t page, const uint8_t* auth);
	SBIResponse* reboot(uint32_t address);
	GetRandomBytesResponse* randomBytes();

	SBIResponse* setActiveSeedLC(uint32_t path[4], uint32_t secLevel);
	GetAddressResponse* getAddressLC(uint32_t slot, uint32_t security, uint32_t keyIndex, bool checksum, bool display);
	PrepareTransfersResponse* prepareTransfersLC(uint32_t timestamp, uint32_t slot, uint32_t security, uint32_t numTX, const uint8_t* auth);

	MCycleResponse* getMCycles();
};





#endif /* SBI_H_ */
